package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.dao.SkuInfoMapper;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuSearchService;
import com.github.pagehelper.util.StringUtil;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.*;

/**
 * @ClassName SkuServiceImpl
 * @Description
 * @Author 洛尘
 * @Date 19:18 2019/11/15
 * @Version 2.1
 **/
@Service
public class SkuSearchServiceImpl implements SkuSearchService {

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * @return
     * @author ShortDust
     * @Description mysql数据保存到索引库中
     * @ClassName SkuServiceImpl
     * @date 2019/11/15 20:20
     */
    @Override
    public void inportSkuInfoES() {
        //查询数据
        List<Sku> skuList = skuFeign.findSkusByStatus("1");
        String text = JSON.toJSONString(skuList);

        //把数据转换成skuInfo
        List<SkuInfo> skuInfos = JSON.parseArray(text, SkuInfo.class);
        if (skuInfos != null && skuInfos.size() > 0) {
            for (SkuInfo skuInfo : skuInfos) {
                String spec = skuInfo.getSpec();
                Map<String, Object> specMap = JSON.parseObject(spec, Map.class);
                skuInfo.setSpecMap(specMap);
            }
        }
        //导入索引库中
        skuInfoMapper.saveAll(skuInfos);
    }


    /**
     * @return
     * @author ShortDust
     * @Description 检索
     * @ClassName SkuSearchServiceImpl
     * @date 2019/11/15 21:10
     */
    @Override
    public Map<String, Object> search(Map<String, String> searchMap) {
        //构建检索条件
        NativeSearchQueryBuilder builder = builderBasiQuery(searchMap);
        //根据关键字检索
        Map<String, Object> reslutMap = searchForPage(builder);
        //构建分组结果
        Map<String, Object> map = searchForOrder(builder, reslutMap);
        reslutMap.putAll(map);

      /*  //根据品牌进行分类
        List<String> brandList = serchForBrandName(builder);
        reslutMap.put("brandList", brandList);
        //根据商品进行分类
        List<String> CategoryList = serchForCategory(builder);
        reslutMap.put("CategoryList", CategoryList);
        //根据规格查询分类
        Map<String, Set<String>> specMap = serchForSpec(builder, reslutMap);
        reslutMap.putAll(specMap);*/

        return reslutMap;
    }

    //构建分组结果
    private Map<String, Object> searchForOrder(NativeSearchQueryBuilder builder, Map<String, Object> reslutMap) {
        builder.addAggregation(AggregationBuilders.
                terms("specName").field("spec.keyword").
                size(Math.toIntExact((Long) reslutMap.get("totalElements"))));
        builder.addAggregation(AggregationBuilders.
                terms("categoryName").field("categoryName").
                size(Math.toIntExact((Long) reslutMap.get("totalElements"))));
        builder.addAggregation(AggregationBuilders.
                terms("brandName").field("brandName").
                size(Math.toIntExact((Long) reslutMap.get("totalElements"))));
        //根据条件进行检索，响应，封装高亮条件，
        AggregatedPage<SkuInfo> aggregatedPage =
                elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        Aggregations aggregations = aggregatedPage.getAggregations();

        //获取分组结果
        List<String> spec = groupResult(aggregations, "specName");
        List<String> categoryList = groupResult(aggregations, "categoryName");
        List<String> brandList = groupResult(aggregations, "brandName");
        Map<String, Set<String>> specMap = pullMap(spec);

        Map<String, Object> map = new HashMap<>();
        map.put("categoryList",categoryList);
        map.put("specMap",specMap);
        map.put("brandList",brandList);
        return map;
    }
    //获取分组结果
    private List<String> groupResult(Aggregations aggregations, String groupName) {
        StringTerms stringTerms = aggregations.get(groupName);
        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
        ArrayList<String> list = new ArrayList<>();
        if (buckets.size()>0 && buckets!=null) {
            for (StringTerms.Bucket bucket : buckets) {
                list.add(bucket.getKeyAsString());
            }
        }
        return list;
    }


    //根据规格查询分类
    private Map<String, Set<String>> serchForSpec(NativeSearchQueryBuilder builder, Map<String, Object> reslutMap) {
        //"手机屏幕尺寸":"5寸","网络":"联通2G","颜色":"红","测试":"实施","机身内存":"16G","存储":"32G","像素":"300万像素"}
        builder.addAggregation(AggregationBuilders.terms("specName").field("spec.keyword").size(Math.toIntExact((Long) reslutMap.get("totalElements"))));
//        builder.addAggregation(AggregationBuilders.terms("specName").field("spec.keyword").size(10000));
        ////根据条件进行检索，响应，封装高亮条件，
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        Aggregations aggregations = aggregatedPage.getAggregations();

        StringTerms stringTerms = aggregations.get("specName");
        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();

        List<String> list = new ArrayList<>();
        if (buckets != null && buckets.size() > 0) {
            for (StringTerms.Bucket bucket : buckets) {
                String keyAsString = bucket.getKeyAsString();
                list.add(keyAsString);
            }
        }
        //把数据存入map中
        Map<String, Set<String>> map = pullMap(list);

        return map;
    }

    //把数据存入map中
    private Map<String, Set<String>> pullMap(List<String> list) {
        Map<String, Set<String>> map = new HashMap<>();
        for (String spec : list) {
            Map specMap = JSON.parseObject(spec, Map.class);
            /**
             * Set<Entry<T,V>> entrySet()
             * 该方法返回值就是这个map中各个键值对映射关系的集合。
             * 可使用它对map进行遍历。
             */
            Set<Map.Entry<String, String>> entries = specMap.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
                Set<String> set = map.get(key);
                if (set == null) {
                    set = new HashSet<>();
                }
                set.add(value);
                map.put(key, set);
            }
        }
        return map;
    }

    //根据商品进行分类
    private List<String> serchForCategory(NativeSearchQueryBuilder builder) {
        //juhe
        builder.addAggregation(AggregationBuilders.terms("CategoryName").field("categoryName"));
        //封装结果
        AggregatedPage<SkuInfo> brand = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        Aggregations aggregations = brand.getAggregations();
        //封装结果集
        StringTerms stringTerms = aggregations.get("CategoryName");
        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
        List<String> list = new ArrayList<>();
        if (buckets != null && buckets.size() > 0) {
            for (StringTerms.Bucket bucket : buckets) {
                list.add(bucket.getKeyAsString());
            }
        }
        return list;
    }

    //根据品牌进行分类
    private List<String> serchForBrandName(NativeSearchQueryBuilder builder) {
        // 这里既是分组（聚合）
        // select brand_name from tb_sku where name like "%小米%" group by brand_namebrandName
        builder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        //封装结果
        AggregatedPage<SkuInfo> brand = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        //分组结果集
        Aggregations aggregations = brand.getAggregations();
        StringTerms stringTerms = aggregations.get("skuBrand");
        List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
        List<String> list = new ArrayList<>();
        if (buckets != null && buckets.size() > 0) {
            for (StringTerms.Bucket bucket : buckets) {
                String keyAsString = bucket.getKeyAsString();
                list.add(keyAsString);
            }
        }
        return list;
    }

    /**
     * 根据关键字检索
     * @param builder
     * @return
     */
    private Map<String, Object> searchForPage(NativeSearchQueryBuilder builder) {
        //关键字进行高亮显示
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");
        field.preTags("<font color='red'>");
        field.postTags("</font>");

        builder.withHighlightFields(field);
        SearchResultMapper searchResultMapper = new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                List<T> content = new ArrayList<>();
                //获取高亮结果集
                SearchHits hits = response.getHits();
                if (hits != null) {
                    for (SearchHit hit : hits) {
                        //获取普通的结果集
                        String result = hit.getSourceAsString();
                        SkuInfo skuInfo = JSON.parseObject(result, SkuInfo.class);
                        HighlightField highlightField = hit.getHighlightFields().get("name");
                        if (highlightField != null) {
                            //获取高亮结果
                            Text[] texts = highlightField.getFragments();
                            skuInfo.setName(texts[0].toString());
                        }
                        content.add((T) skuInfo);
                    }
                }
                return new AggregatedPageImpl<>(content, pageable, hits.getTotalHits());
            }
        };

        //创建一次会话
        NativeSearchQuery build = builder.build();
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(build, SkuInfo.class, searchResultMapper);

        List<SkuInfo> rows = page.getContent();               //结果集
        long totalElements = page.getTotalElements();         //总条数
        int totalPages = page.getTotalPages();                //总页数
        int pageNumber = build.getPageable().getPageNumber(); //当前的页码
        int pageSize = build.getPageable().getPageSize();     //每页显示的条数

        Map<String, Object> map = new HashMap<>();
        map.put("rows", rows);
        map.put("totalElements", totalElements);
        map.put("totalPages", totalPages);
        map.put("pageNum",pageNumber);
        map.put("pageSize",pageSize);

        return map;
    }


    //检索条件
    private NativeSearchQueryBuilder builderBasiQuery(Map<String, String> searchMap) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //封装过滤条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //QueryBuilder queryBuilder = QueryBuilders.f
        if (searchMap != null) {
            //关键字检索
            String keyword = searchMap.get("keyword");
            if (!StringUtil.isEmpty(keyword)) {
                builder.withQuery(QueryBuilders.matchQuery("name", keyword));
            }
            //根据品牌检索
            String brand = searchMap.get("brand");
            if (!StringUtil.isEmpty(brand)) {
                boolQuery.must(QueryBuilders.matchQuery("brandName", brand));
            }
            //分类检索
            String category = searchMap.get("category");
            if (!StringUtil.isEmpty(category)) {
                boolQuery.must(QueryBuilders.matchQuery("categoryName", category));
            }
            //根据规格进行检索
            Set<String> keys = searchMap.keySet();

                for (String key : keys) {
                    if (key.startsWith("spec_")) {
                        String value =searchMap.get(key).replace("\\","");
//                    boolQuery.must(QueryBuilders.matchQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
                        boolQuery.filter(QueryBuilders.matchQuery("specMap."+ key.substring(5)+ ".keyword",value));
                    }
                }


            //价格区间过滤
            String price = searchMap.get("price");
            if (!StringUtil.isEmpty(price)) {
                String[] prices = price.split("-");
                //max min
                boolQuery.must(QueryBuilders.rangeQuery("price").gte(prices[0]));
                if (prices.length == 2) {
                    boolQuery.must(QueryBuilders.rangeQuery("price").lte(prices[1]));
                }
//                boolQuery.must(QueryBuilders.rangeQuery("price").gte(prices[0]).lte(prices[1]));
            }
            //System.out.println("-------------");
            //搜索排序
            String sortRule = searchMap.get("sortRule");    //排序规则
            String sortFiled = searchMap.get("sortField");  //排序字段
            if (!StringUtil.isEmpty(sortFiled)) {
                builder.withSort(SortBuilders.fieldSort(sortFiled).order(SortOrder.valueOf(sortRule)));
            }
            //搜索分页
            String num = searchMap.get("pageNum");    //起始行
            if (StringUtil.isEmpty(num)) {
                num = "1";
            }
            int pageNum = Integer.parseInt(num) - 1;
            builder.withPageable(PageRequest.of(pageNum, 40));
            //System.out.println("--------------------");
            //添加过滤条件
            builder.withFilter(boolQuery);
        }


        return builder;
    }
}
